#pragma once
#include <brpc/channel.h>
#include <string>
#include <vector>
#include <unordered_map>
#include <mutex>
#include "logger.hpp"
namespace wsll{
// 1.封装单个服务的信道管理类：建立起服务和信道的映射关系.注意可能一个服务可能对应多个channel，即一个服务有多个server可以提供服务
class ServiceChannel
{
public:
    using ptr = std::shared_ptr<ServiceChannel>;
    using ChannelPtr = std::shared_ptr<brpc::Channel>;
    ServiceChannel(const std::string &name) : _service_name(name), _index(0)
    {
    }
    // 一个服务节点（也就是一个server）上线了，就调append，传入自己的host
    void append(const std::string &host)
    {
        auto channel = std::make_shared<brpc::Channel>();
        brpc::ChannelOptions options;
        options.connect_timeout_ms = -1; // 设置连接超时时间，-1表示一直等待
        options.timeout_ms = -1;         // 设置rpc请求超时时间，-1表示一直等待
        options.max_retry = 3;           // 设置最大重试次数
        options.protocol = "baidu_std";  // 序列化协议，默认使用baidu_std
        int ret = channel->Init(host.c_str(), &options);
        if (ret == -1)
        {
            LOG_ERROR("初始化{} - {}节点服务信道失败", _service_name, host);
            return;
        }
        std::unique_lock<std::mutex> lock(_mutex);
        _hosts[host] = channel;
        _channels.push_back(channel);
    }
    // 注意其实remove的概率很低，因此选择数据结构的时候主要考虑方便添加
    void remove(const std::string &host)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _hosts.find(host);
        if (it == _hosts.end())
        {
            LOG_WARN("移除{} - {}节点服务信道失败:没有找到信道信息", _service_name, host);
        }
        for (auto vit = _channels.begin(); vit != _channels.end(); vit++)
        {
            if (*vit == it->second)
            {
                _channels.erase(vit);
                break;
            }
        }
        _hosts.erase(it);
    }
    // 通过RR轮转策略选择一个channel用于发起对应服务的RPC调用
    ChannelPtr choose()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if (_channels.empty())
        {
            LOG_ERROR("没有可用的{}服务节点", _service_name);
            return nullptr;
        }
        int32_t idx = _index % _channels.size();
        _index++;
        return _channels[idx];
    }

private:
    std::mutex _mutex; // 加锁来保证多线程安全
    int32_t _index;    // RR轮转策略中轮转下标计数器
    std::string _service_name;
    std::vector<ChannelPtr> _channels;                  // 为了方便轮转，使用vector存储channel，而不是map之类的
    std::unordered_map<std::string, ChannelPtr> _hosts; // 建立主机与信道的映射关系，这样以后可以通过host名来找到对应的信道
};
// 2.封装服务的信道管理类：也就是需要管理1中实现的ServiceChannel类的多个对象，也即管理多个服务的信道
class ServiceManager
{
public:
    using ptr = std::shared_ptr<ServiceManager>;
    ServiceManager() {}
    ServiceChannel::ChannelPtr choose(const std::string &service_name) // 输入一个服务的name，返回这个服务有用的信道
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto sit = _services.find(service_name);
        if (sit == _services.end())
        {
            LOG_ERROR("没有找到{}服务的节点", service_name);
            return nullptr;
        }
        return sit->second->choose();
    }
    // 先声明，我关注哪些服务的上下线，不关注的不需要管理
    void declared(const std::string &service_name)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _services_I_care.insert(service_name);
    }
    void onServiceOnline(const std::string &service_instance, const std::string &host) // 某个服务新上线了一个主机，就调用这个函数(就是etcd的回调函数) 管理服务节点
    {
        std::string service_name = getServiceName(service_instance);
        ServiceChannel::ptr service;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto fit = _services_I_care.find(service_name);
            if (fit == _services_I_care.end())
            {
                LOG_DEBUG("{}-{}服务节点上线，但是不在关注列表中", service_name, host);
                return;
            }
            // 先获取管理对象，没有就创建
            auto sit = _services.find(service_name);
            if (sit == _services.end())
            {
                service = std::make_shared<ServiceChannel>(service_name); // 新创建一个服务信道
                _services[service_name] = service;
            }
            else
            {
                service = sit->second;
            }

        } // 接下来的操作是不需要加锁的
        if (!service)
        {
            LOG_ERROR("新增{}服务的节点失败", service_name);
            return;
        }
        service->append(host);
        LOG_DEBUG("{}-{}服务节点上线，进行添加管理", service_name, host);
    }
    void onServiceOffline(const std::string &service_instance, const std::string &host) // 某个服务下线了一个主机，就调用这个函数 删除这个信道理里的指定节点
    {
        std::string service_name = getServiceName(service_instance);
         ServiceChannel::ptr service;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto fit = _services_I_care.find(service_name);
            if (fit == _services_I_care.end())
            {
                LOG_DEBUG("{}-{}服务节点下线，但是不在关注列表中", service_name, host);
                return;
            }
            // 先获取管理对象，没有就报警
            auto sit = _services.find(service_name);
            if (sit == _services.end())
            {
                LOG_WARN("删除的{}服务节点不存在", service_name);
                return;
            }
            service = sit->second;
        } // 接下来的操作是不需要加锁的
        service->remove(host);
        LOG_DEBUG("{}-{}服务节点下线，进行删除管理", service_name, host);
    }
    private:
    std::string getServiceName(const std::string &service_instance)
    {
        auto pos = service_instance.find_last_of('/');
        return service_instance.substr(0,pos);
    }
private:
    std::mutex _mutex;
    std::unordered_set<std::string> _services_I_care;
    std::unordered_map<std::string, ServiceChannel::ptr> _services;
};
}